package com.iplay.web.hotel;

import java.text.ParseException;
import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.iplay.component.date.FeastBookingDateFormatter;
import com.iplay.dto.ApiResponse;
import com.iplay.dto.hotel.HotelDTO;
import com.iplay.dto.hotel.HotelDiscountDTO;
import com.iplay.dto.hotel.SimplifiedHotelAdminDTO;
import com.iplay.dto.hotel.SimplifiedHotelDTO;
import com.iplay.dto.review.ReviewDTO;
import com.iplay.service.exception.InvalidRequestParametersException;
import com.iplay.service.exception.ResourceNotFoundException;
import com.iplay.service.hotel.HotelService;
import com.iplay.service.hotel.discount.HotelDiscountService;
import com.iplay.service.review.ReviewService;
import com.iplay.vo.common.EntityDeletionVO;
import com.iplay.vo.common.FileDeletionVO;
import com.iplay.vo.common.PostFilesVO;
import com.iplay.vo.hotel.HotelFilterVO;
import com.iplay.vo.hotel.PostBanquetHallVO;
import com.iplay.vo.hotel.PostFeastVO;
import com.iplay.vo.hotel.PostHotelVO;
import com.iplay.web.configuration.PaginationConfig;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

@RestController
@RequestMapping("/api/hotels")
@Validated
public class HotelController {
	@Autowired
	private HotelService hotelService;

	@Autowired
	private ReviewService reviewService;

	@Autowired
	private HotelDiscountService hotelDiscountService;

	@Autowired
	private FeastBookingDateFormatter dateFormatter;

	@ApiOperation(notes = "管理員新增一個酒店，返回酒店id", value = "")
	@PostMapping
	@PreAuthorize("hasRole('ADMIN')")
	public int addHotel(@Valid @ApiParam("酒店实体，属性包括：name, cityOfAddress, districtOfAddress,"
			+ "streetOfAddress, description, contact, telephone, email") @RequestBody PostHotelVO postHotelVO) {
		postHotelVO.setId(-1);
		return hotelService.saveHotel(postHotelVO);
	}

	@ApiOperation(notes = "管理員更新一個酒店，id为必填项，返回酒店id", value = "")
	@PutMapping("/{id}")
	@PreAuthorize("hasRole('ADMIN')")
	public int updateHotel(@ApiParam("酒店id") @PathVariable int id,
			@Valid @ApiParam("酒店实体，属性包括：name, cityOfAddress, districtOfAddress,"
					+ "streetOfAddress, description, contact, telephone, email") @RequestBody PostHotelVO postHotelVO) {
		postHotelVO.setId(id);
		int rs = hotelService.saveHotel(postHotelVO);
		if (rs == -1)
			throw new ResourceNotFoundException("Hotel with id: " + id + " doesn't exist");
		return id;
	}

	@ApiOperation(notes = "管理員删除一个酒店，id为必填项，返回boolean", value = "")
	@DeleteMapping("/{id}")
	@PreAuthorize("hasRole('ADMIN')")
	public boolean deleteHotel(@ApiParam("酒店id") @PathVariable int id) {
		return hotelService.deleteHotel(id);
	}

	@ApiOperation(notes = "管理員批量删除酒店，返回boolean", value = "")
	@DeleteMapping
	@PreAuthorize("hasRole('ADMIN')")
	public boolean deleteHotel(@Valid @RequestBody @ApiParam("酒店id集合") EntityDeletionVO entityDeletionVO) {
		return hotelService.deleteHotels(entityDeletionVO);
	}

	@ApiOperation(notes = "分页展示酒店,返回酒店的List，默认每页展示10条数据，并且按照评价排序<br>"
			+ "sort=minimumPrice,desc&sort=maximumPrice,desc 按照价格降序排列<br>" + "sort=rating,asc 按照评价升序排序<br>"
			+ "sort=rating,desc 按照评价降序排序<br>" + "sort=numOfOrders,desc 按照订单数量降序排序<br>"
			+ "sort=numOfOrders,asc 按照订单数量升序排序<br>" + "min_rating=4.0 评分大于4.0" + "min_price=100.0 价格大于等于100.0"
			+ "max_price=200.0 价格小于等于200.0", value = "")
	@GetMapping
	public List<SimplifiedHotelDTO> listHotelsForUser(
			@ApiParam("分页参数，可选。例子：page=0&size=15&sort=minimumPrice,desc。"
					+ "默认情况下size=20, sort=orderTime,desc.") @PageableDefault(value = PaginationConfig.HOTELS_PER_PAGE_FOR_ORDINARY_USER, sort = {
							"rating" }, direction = Sort.Direction.DESC) Pageable pageable,
			@ApiParam("评分下限") @RequestParam(name = "min_rating", required = false) Double minimumRating,
			@ApiParam("价格下限") @RequestParam(name = "min_price", required = false) Double minimumPrice,
			@ApiParam("价格上限") @RequestParam(name = "max_price", required = false) Double maximumPrice,
			@ApiParam("酒店名") @RequestParam(name = "name", required = false) String name) {
		HotelFilterVO filter = new HotelFilterVO(minimumRating, minimumPrice, maximumPrice,
				name != null ? "%" + name + "%" : name);
		return hotelService.listHotelsByFilter(filter, pageable);
	}

	@ApiOperation(notes = "管理員獲取酒店列表", value = "")
	@GetMapping("/all")
	@PreAuthorize("hasRole('ADMIN')")
	public List<SimplifiedHotelAdminDTO> listHotelsForAdmin() {
		return hotelService.listHotel();
	}

	@ApiOperation(notes = "根据酒店Id获得酒店详细信息", value = "")
	@GetMapping("/{id}")
	public HotelDTO findHotelById(@ApiParam("酒店id") @PathVariable int id) {
		HotelDTO hotel = hotelService.findHotelById(id);
		if (hotel == null)
			throw new ResourceNotFoundException("Hotel with id: " + id + " doesn't exist");
		return hotel;
	}

	@ApiOperation(notes = "管理員新增一個宴会厅，返回宴会厅id", value = "")
	@PostMapping("/{id}/banquet_halls")
	@PreAuthorize("hasRole('ADMIN')")
	public int addBanquetHall(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@Valid @RequestBody @ApiParam("宴会厅实体，属性包括：name, area, minimumTables, maximumTables, minimumPrice, height, columns(String), "
					+ "shape(String), actualArea(String), colorOfTablecloth, extraInfo(String，配置使用;连接), files") PostBanquetHallVO banquetHallVO) {
		int id = hotelService.addBanquetHall(banquetHallVO, hotelId);
		if (id == -1)
			throw new ResourceNotFoundException("Hotel with id:" + hotelId + " doesn't exist");
		return id;
	}

	@ApiOperation(notes = "管理員新增一個宴席，返回宴席id", value = "")
	@PostMapping("/{id}/feasts")
	@PreAuthorize("hasRole('ADMIN')")
	public int addFeast(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@Valid @RequestBody @ApiParam("宴席实体，属性包括：name, price, courses(String，菜肴使用;连接)") PostFeastVO feastVO) {
		int id = hotelService.addFeast(feastVO, hotelId);
		if (id == -1)
			throw new ResourceNotFoundException("Hotel with id:" + hotelId + " doesn't exist");
		return id;
	}

	@ApiOperation(notes = "管理员删除图片", value = "")
	@DeleteMapping("/{id}/pictures")
	@PreAuthorize("hasRole('ADMIN')")
	public boolean[] deletePictures(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@Valid @RequestBody @ApiParam("文件名集合") FileDeletionVO pictures) {
		boolean[] rs = hotelService.deletePictures(hotelId, pictures);
		if (rs == null)
			throw new ResourceNotFoundException("Hotel with id:" + hotelId + " doesn't exist");
		return rs;
	}

	@ApiOperation(notes = "管理员上传酒店图片，返回所有增加的图片uri", value = "")
	@PostMapping("/{id}/pictures")
	@PreAuthorize("hasRole('ADMIN')")
	public String[] savePictures(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@ApiParam("MultipartFile[] files") @Valid PostFilesVO postFilesVO) {
		String[] uris = hotelService.savePictures(hotelId, postFilesVO);
		if (uris == null)
			throw new ResourceNotFoundException("Hotel with id:" + hotelId + " doesn't exist");
		return uris;
	}

	@ApiOperation(notes = "用户分页获得酒店的评论", value = "")
	@GetMapping("/{id}/reviews")
	public Page<ReviewDTO> savePictures(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@ApiParam("分页参数，可选。例子：page=0&size=10&sort=reviewTime,desc。"
					+ "默认情况下size=10, sort=reviewTime,desc.") @PageableDefault(value = PaginationConfig.REVIEWS_PER_PAGE, sort = {
							"reviewTime" }, direction = Sort.Direction.DESC) Pageable pageable) {
		return reviewService.listReviews(hotelId, pageable);
	}

	@ApiOperation(notes = "用户获得酒店的折扣信息，默認返回最近的5条折扣信息", value = "")
	@GetMapping("/{id}/discounts")
	public List<HotelDiscountDTO> listDiscounts(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@ApiParam("起始日期，app端将此属性设置为当天即可。since=2017.12.11") @RequestParam(name = "since", required = false) String startDate,
			@ApiParam("结束日期") @RequestParam(name = "until", required = false) String endDate,
			@ApiParam("分页参数，可选。例子：page=0&size=3&sort=date,asc。"
					+ "默认情况下page=0, size=5, sort=date,asc.") @PageableDefault(value = 5, sort = {
							"date" }, direction = Sort.Direction.ASC) Pageable pageable) {
		long start = -1, end = Long.MAX_VALUE;
		if (startDate != null) {
			try {
				start = dateFormatter.parseToDateWithDefaultDateFormat(startDate).getTime();
			} catch (ParseException e) {
				throw new InvalidRequestParametersException(
						"The date format must be " + dateFormatter.getDefaultDateFormat() + "!");
			}
		}
		if (endDate != null) {
			try {
				end = dateFormatter.parseToDateWithDefaultDateFormat(endDate).getTime();
			} catch (ParseException e) {
				throw new InvalidRequestParametersException(
						"The date format must be " + dateFormatter.getDefaultDateFormat() + "!");
			}
		}
		return hotelDiscountService.listDiscounts(hotelId, start, end, pageable);
	}

	@ApiOperation(notes = "管理员上傳包含酒店折扣信息的excel文件, 返回ApiResponse对象，ApiResponse.success为false表明解析excel失败，ApiResponse.data中包含了失败原因。success為true表明excel上傳成功", value = "")
	@PostMapping("/{id}/discounts")
	@PreAuthorize("hasRole('ADMIN')")
	public ApiResponse<String> saveDiscounts(@ApiParam("酒店id") @PathVariable("id") int hotelId,
			@ApiParam("包含折扣信息的excel文件") @Valid @NotNull MultipartFile file) {
		return hotelDiscountService.saveHotelDiscount(hotelId, file);
	}

}